package com.cyj.dream.auth.config;

import com.cyj.dream.auth.handler.*;
import com.cyj.dream.auth.persistence.service.impl.CustomUserServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

/**
 * @Description: WebSecurity配置
 * @BelongsProject: DreamChardonnay
 * @BelongsPackage: com.cyj.dream.auth.config
 * @Author: ChenYongJia
 * @CreateTime: 2021-09-30 10:27
 * @Email: chen87647213@163.com
 * @Version: 1.0
 */
@Configuration
@EnableWebSecurity
// https://blog.csdn.net/qq_36525300/article/details/101645565
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Autowired
    private CustomUserServiceImpl userDetailsService;

    /**
     * 自定义登录成功处理器
     */
    @Autowired
    private MyAuthenticationSuccessHandler userLoginSuccessHandler;

    /**
     * 自定义登录失败处理器
     */
    @Autowired
    private MyAuthenticationFailureHandler userLoginFailureHandler;

    /**
     * 自定义注销成功处理器
     */
    @Autowired
    private UserLogoutSuccessHandler userLogoutSuccessHandler;

    /**
     * 自定义暂无权限处理器
     */
    @Autowired
    private UserAuthAccessDeniedHandler userAuthAccessDeniedHandler;

    /**
     * 自定义未登录的处理器
     */
    @Autowired
    private UserAuthenticationEntryPointHandler userAuthenticationEntryPointHandler;

    @Bean
    public BCryptPasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Override
    @Bean
    public UserDetailsService userDetailsService() {
        return new CustomUserServiceImpl();
    }

    /**
     * 配置登录验证逻辑
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService())
                .passwordEncoder(passwordEncoder());
    }

    /**
     * 认证管理--需要配置这个支持password模式
     * <p>
     * support password grant type
     *
     * @return 认证管理对象
     * @throws Exception 认证异常信息
     */
    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    /**
     * 安全请求配置，这里配置的是Security的部分，请求全部通过，安全拦截在资源服务器配置
     * <p>
     * http安全配置
     *
     * @param http http安全对象
     * @throws Exception http安全异常信息
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //配置允许的请求以及跨域问题
        http.authorizeRequests()
                // 所有请求都可以访问，本地开发打开下面一行的注释
                //.antMatchers("/**").permitAll()
                //不进行权限验证的请求或资源(从配置文件中读取)，本地开发可以，将本行注释掉
                .antMatchers("/webjars/**", "/js/**", "/css/**", "/images/*", "/fonts/**", "/**/*.png", "/**/*.jpg",
                        "/static/**","/login", "/oauth/**")
                // .anyRequest()
                .permitAll()
                .and()
                //配置未登录自定义处理类
                .httpBasic().authenticationEntryPoint(userAuthenticationEntryPointHandler)
                .and()
                .csrf()
                .disable()
                // 配置登录地址
                .formLogin()
                // 本地
                //.loginPage("/login/userLogin")
                //.loginProcessingUrl("/login/userLogin")
                .loginPage("/login")
                //配置登录成功自定义处理类
                .successHandler(userLoginSuccessHandler)
                //配置登录失败自定义处理类
                .failureHandler(userLoginFailureHandler)
                .permitAll()
                .and()
                //配置登出地址
                .logout()
                // /userInfo/loginOutByToken
                .logoutUrl("/login/loginOut")
                //配置用户登出自定义处理类
                .logoutSuccessHandler(userLogoutSuccessHandler)
                .and()
                //配置没有权限自定义处理类
                .exceptionHandling()
                .accessDeniedHandler(userAuthAccessDeniedHandler)
                .and()
                // 开启跨域
                .cors()
                .and()
                // 取消跨站请求伪造防护
                .csrf().disable();
                /*.cors().and()
                .addFilterAt(ignoreLogoutFilter, LogoutFilter.class);*/
        // 为了可以使用 iframe 内嵌页面加载
        http.headers().frameOptions().sameOrigin();
        // 基于Token不需要session
        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
        // 禁用缓存
        http.headers().cacheControl();
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        // 设置忽略资源
        web.ignoring().antMatchers(
                "/error",
                "/v2/api-docs/**",
                "/favicon.ico",
                "/css/**",
                "/js/**",
                "/images/*",
                "/fonts/**",
                "/**/*.png",
                "/**/*.jpg")
                // 不拦截 swagger2 所进行的配置
                .antMatchers("/templates/**")
                .antMatchers("/static/**")
                .antMatchers("/webjars/**")
                .antMatchers("/swagger-ui.html/**")
                .antMatchers("/v2/**")
                .antMatchers("/doc.html")
                .antMatchers("/api-docs-ext/**")
                .antMatchers("/swagger-resources/**");
        ;
    }

    /*
     * OK ，关于这个配置我要多说两句：
     *
     * 1.首先当我们要自定义Spring Security的时候我们需要继承自WebSecurityConfigurerAdapter来完成，相关配置重写对应
     * 方法即可。 2.我们在这里注册CustomUserService的Bean，然后通过重写configure方法添加我们自定义的认证方式。
     * 3.在configure(HttpSecurity http)方法中，我们设置了登录页面，而且登录页面任何人都可以访问，然后设置了登录失败地址，也设置了注销请求，
     * 注销请求也是任何人都可以访问的。
     * 4.permitAll表示该请求任何人都可以访问，.anyRequest().authenticated(),表示其他的请求都必须要有权限认证。
     * 5.这里我们可以通过匹配器来匹配路径，比如antMatchers方法，假设我要管理员才可以访问admin文件夹下的内容，我可以这样来写：.
     * antMatchers("/admin/**").hasRole("ROLE_ADMIN")，也可以设置admin文件夹下的文件可以有多个角色来访问，
     * 写法如下：.antMatchers("/admin/**").hasAnyRole("ROLE_ADMIN","ROLE_USER")
     * 6.可以通过hasIpAddress来指定某一个ip可以访问该资源,假设只允许访问ip为210.210.210.210的请求获取admin下的资源，
     * 写法如下.antMatchers("/admin/**").hasIpAddress("210.210.210.210")
     * 7.更多的权限控制方式参看源码：
     */


}
